;代码改进
;接线不变 8255的C口做输出 C接在7个LED上 
;按下1键 从最右边开始亮灯(其余灯灭) 每次朝左侧亮一个灯(其余灯灭)  
;          当亮灯到达最左侧后 变成朝右侧亮灯(每次亮一个 其余灯灭)
;按下3键 退出
; 即实现了折返式的循环亮灯
DATA SEGMENT
;-接口区-
IO8255C EQU 28AH              ;8255 PC口
IO8255D EQU 28BH              ;8255 控制端口 
;-提示信息区-
MESS1 DB 'Press 1,left-right return',0DH,0AH     ;左侧开始的折返循环亮灯
      DB 'Press 3,quit',0DH,0AH,'$'
MESS2 DB 'Wrong key!',0DH,0AH,'$'
MESS3 DB 0DH,0AH,'Press any to come back Menu.',0DH,0AH,'$'
DATA ENDS

CODE SEGMENT
ASSUME CS:CODE,DS:DATA 
START:
;初始化设置
      ;数据段初始化
      MOV AX,DATA 
      MOV DS,AX
      ;向8255控制端口写80H(1000 0000) 
      ;(1)特征位 (00)A方式0 (0)A输出 
      ;(0)C高4位输出 (0)B方式0 (0)B输出 (0)C低4位输出
      ;即ABC均做输出 工作方式为0
      MOV DX,IO8255D
      MOV AL,80H
      OUT DX,AL 

;BG:  显示功能菜单 并根据用户按键跳转指定分支
      ;显示菜单
BG:   LEA DX,MESS1 
      MOV AH ,9 
      INT 21H      
      ;此处8号功能 输入字符到AL 无回显 
      ;区别2号功能 输入字符到AL 有回显
      MOV AH,8 
      INT 21H          ;此处不回显 意味着按键即生效 而非是输入字符生效
      ;返回菜单提示
      PUSH AX         ;保护AL
      LEA DX,MESS3    ;提示用户按任意键返回 与后方的6号功能首尾呼应!
      MOV AH,9
      INT 21H 
      POP AX           ;恢复AL
      ;根据用户按下的键 确定执行的分支
      CMP AL,'1' 
      JZ ZXH            ;按下1 左侧开始的折返循环闪烁
      ; CMP AL,'2'        
      ; JZ YXH            ;2键功能暂时停用 留待以后补充随机停灯
      CMP AL,'3' 
      ;此处没用JZ QIT 是因为JZ跳转范围有限
      ;当代码较长时 JZ失效 因此需要JMP
      ;故而此处先用JNZ跳转近处不等于4的情况
      ;后面再跟上跳转范围更大但没有条件检测功能的JMP
      ;应当积累这种解决方案
      JNZ WX            ;不是4 提示错误信息
      JMP QIT           ;是4 则退出


;WX: 提示用户按键错误 并返回BG重来
WX:   LEA DX,MESS2 
      MOV AH,09H 
      INT 21H 
      JMP BG 

;ZXH  左侧开始的有折返的循环闪烁功能模块
      ;向8255的C口写入01H(0000 0001) 
      ;设置LED灯初态为只有最右侧亮一灯
ZXH:  MOV BX,0          ;将BX用于保存FLAG 初始为0 
      MOV DX,IO8255C 
      MOV AL,01H
BB:   OUT DX,AL
      ;闪烁间隔时间(延迟) 
      MOV CX,0FFFFH 
L1:   LOOP L1 
      ;检测标志 确定左移还是右移
      CMP BX,0
      JNZ  RST      ;BX不等于0 则对AL执行右移
      ;BX为0 继续将AL中的数逻辑左移 获得下一个LED显示态对应的数值

LST:  SHL AL,1 ;普通左移的好处是当移动到头时 会变成0 这时0并不会被显示 
      JZ SETR  ;若变成0 则修改标志BX为1 并设置AL为(0100 0000) 40H
      JMP TESTI;反之则跳到TESTI 正常判定与输出

RST:  SHR AL,1 ;普通右移的好处是当移动到头时 会变成0 这时0并不会被显示
      JZ SETL  ;若变成0 则修改标志BX为0 并设置AL为(0000 0010)02H  
      JMP TESTI;反之则跳TESTI 正常判断与输出
      ;设置右移模式
SETR: MOV BX,1
      MOV AL,40H
      JMP TESTI      
      ;设置左移模式
SETL: MOV BX,0
      MOV AL,02H
;TESTI: 检测是否有用户输入 若有返回菜单 不等待用户!
TESTI: PUSH AX     ;保护AL(LED显示态)和DX(C端口地址) 
      PUSH DX 
      MOV DL,0FFH ;DL=0FFH(输入请求)
      MOV AH,06H  ;6号功能(直接控制台I/O) 
      INT 21H     ;尝试从标准输入中获取用户输入到AL
                  ;若用户自上次输入后再无输入 则返回0到AL
      JNZ BG      ;AL不为0 则跳BG重来
                  ;此处跳转与 BG模块下的"按任意键返回菜单"首尾呼应!
      POP DX      ;恢复DX(C端口地址)和AL(LED显示态)
      POP AX 
      JMP BB      ;写C端口 更新LED的显示状态 

;QIT: 结束程序
QIT:  MOV AH,4CH 
      INT 21H 
CODE ENDS
END START